<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------

namespace app\common\model;

class HttpCurl
{
    private $request_url                         = '';
    private $method                              = 'GET';
    private $response_error_body                 = '';
    private $response_raw_headers                = '';
    private $read_stream                         = '';
    private $read_stream_size                    = '';
    private $read_stream_read                    = '';
    private $seek_position                       = '';
    private $write_stream                        = '';
    private $write_file                          = '';
    private $write_file_handle                   = '';
    private $registered_streaming_read_callback  = '';
    private $registered_streaming_write_callback = '';
    private $time_out                            = 5184000;
    private $connect_timeout                     = 10;

    public function __construct($request_url = '')
    {
        $this->request_url = $request_url;
        return $this;
    }

    public function method($method)
    {
        $this->method = $method;
        return $this;
    }

    public function register_streaming_read_callback($callback)
    {
        $this->registered_streaming_read_callback = $callback;

        return $this;
    }

    public function register_streaming_write_callback($callback)
    {
        $this->registered_streaming_write_callback = $callback;

        return $this;
    }

    public function streaming_header_callback($curl_handle, $header_content)
    {
        $code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE);

        if (isset($this->write_file) && intval($code) / 100 == 2 && !isset($this->write_file_handle)) {
            $this->write_file_handle = fopen($this->write_file, 'w');
            $this->set_write_stream($this->write_file_handle);
        }

        $this->response_raw_headers .= $header_content;
        return strlen($header_content);
    }

    public function streaming_read_callback($curl_handle, $file_handle, $length)
    {
        if ($this->read_stream_read >= $this->read_stream_size) {
            return '';
        }
        if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream)) {
            if (fseek($this->read_stream, $this->seek_position) !== 0) {
                throw new \think\Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.', 10006);
            }
        }

        $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length));
        $this->read_stream_read += strlen($read);

        $out = $read === false ? '' : $read;

        if ($this->registered_streaming_read_callback) {
            call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out);
        }

        return $out;
    }

    public function streaming_write_callback($curl_handle, $data)
    {
        $code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE);

        if (intval($code) / 100 != 2) {
            $this->response_error_body .= $data;
            return strlen($data);
        }

        $length        = strlen($data);
        $written_total = 0;
        $written_last  = 0;

        while ($written_total < $length) {
            $written_last = fwrite($this->write_stream, substr($data, $written_total));

            if ($written_last === false) {
                return $written_total;
            }

            $written_total += $written_last;
        }

        if ($this->registered_streaming_write_callback) {
            call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total);
        }

        return $written_total;
    }

    public function request()
    {
        $curl_handle = curl_init();
        curl_setopt($curl_handle, CURLOPT_URL, $this->request_url);
        curl_setopt($curl_handle, CURLOPT_FILETIME, true);
        curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);
        curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);
        curl_setopt($curl_handle, CURLOPT_HEADER, true);
        curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl_handle, CURLOPT_TIMEOUT, $this->time_out);
        curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout);
        curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);
        curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);
        curl_setopt($curl_handle, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36');
        curl_setopt($curl_handle, CURLOPT_HEADERFUNCTION, array($this, 'streaming_header_callback'));
        curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));

        curl_setopt($curl_handle, CURLOPT_BINARYTRANSFER, 1);
        curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);
        if (isset($this->write_stream) || isset($this->write_file)) {
            curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));
            curl_setopt($curl_handle, CURLOPT_HEADER, false);
        }

    }
}
